/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package formula.node;

import formula.FormulaProblemParams;
import java.util.Hashtable;
import java.util.Set;

/**
 *
 * @author CJ
 */
public abstract class Node {

    protected Node leftNode;
    protected Node rightNode;

    protected int depth;

    public Node(Node left, Node right, int depth){
        this.leftNode = left;
        this.rightNode = right;
        this.depth = depth;
    }

    public Node(int depth){
        this.depth = depth;
    }

    public int getDepth(){
        return this.depth;
    }

    public void setDepth(int depth){
        this.depth = depth;
    }

    public abstract Node clone();
    public abstract double eval(Hashtable<String, Double> varables);
    public abstract boolean reducable();

    public Node getLeft(){
        return this.leftNode;
    }

    public Node getRight(){
        return this.rightNode;
    }

    public static Node getRandomNode(FormulaProblemParams params, int depth){
        if(depth > params.maxDepth && params.maxDepth != -1)
            return getRandomValueNode(params, depth);
        int value = (int)(Math.random() * 2);
        switch(value){
            case 0:
                //Get an Operation Node
                Node result1 = Node.getRandomNode(params, depth+1);
                Node result2 = Node.getRandomNode(params, depth+1);
                Node toReturn = new OperationNode(result1, result2, OperationNodeType.getRandomOperation(), depth);
                return toReturn;
            default:
                Node result =  getRandomValueNode(params, depth);
                return result;
                //Return a value node
        }
    }

    public static Node getRandomValueNode(FormulaProblemParams params, int depth){
        Set<String> constantKeys = params.constantsStorage.keySet();
        double newValue;

        int random = (int) (Math.random() * (params.getDiff() + constantKeys.size() + params.varableNames.size()));
        if (random > params.getDiff() + constantKeys.size()) {
            //Add a varable
            String name = params.varableNames.get((int) (params.varableNames.size() * Math.random()));
            return new ValueNode(params.ERROR, name, depth);
        } else if (random > params.getDiff()) {
            //Add a constant
            String name = constantKeys.toArray(new String[0])[(int) (constantKeys.size() * Math.random())];
            return new ValueNode(params.constantsStorage.get(name), name, depth);
        }
        return new ValueNode(params.minValue + (Math.random() * params.getDiff()), null, depth);
    }

}